﻿////////////////////////////////////////////////////////////////////////////////
// Source Copyright (C) J.Fan
// @author      J.Fan
// @date        2013-01-19
// @revision    Revision in Version Control System
// @remark      
////////////////////////////////////////////////////////////////////////////////

using System.Globalization;
using System.Text;

namespace JsonConverter.Converter
{
    /// <summary>
    /// Convert C# string to JSON stirng
    /// </summary>
    public class StringConverter: IPrimitiveConverter
    {
        public const string EMPTY = "\"\"";

        public string Convert(object value)
        {
            return ToJavascriptString(value.ToString());
        }

        public static string ToJavascriptString(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return EMPTY;
            }

            StringBuilder stringBuilder = new StringBuilder(value.Length + 10);

            // add begin quote mark
            stringBuilder.Append('"');

            int i = 0;          // start position of value to append to stringBuilder
            int n = 0;          // number of chars in value to append to stringBuilder
            string s = null;    // record escapable char to append to stringBuilder
            foreach (char c in value)
            {
                switch (c)
                {
                    case '"':
                        s = "\\\"";
                        break;
                    case '/':
                        s = "\\/";
                        break;
                    case '\b':
                        s = "\\b";
                        break;
                    case '\f':
                        s = "\\f";
                        break;
                    case '\n':
                        s = "\\n";
                        break;
                    case '\r':
                        s = "\\r";
                        break;
                    case '\t':
                        s = "\\t";
                        break;
                    case '\\':
                        s = "\\\\";
                        break;
                    default:
                        if (c < ' ')
                        {
                            // flush cache (n chars from index i) to stringBuilder,
                            // then change invisible char to unicode strings and append to stringBuilder.
                            stringBuilder.Append(value, i, n)
                                .Append("\\u")
                                .Append(((int) c).ToString("x4", CultureInfo.InvariantCulture));
                            i += n;
                            n = 0;
                            i++;
                        }
                        else
                        {
                            // continue cache the char
                            n++;
                        }
                        break;
                }

                if (s != null)
                {
                    // escapable char is found, so flush cache
                    // then append the escaped string to stringBuilder
                    stringBuilder.Append(value, i, n).Append(s);
                    i += n;
                    n = 0;
                    i++;
                    s = null;
                }
            }

            if (n > 0)
            {
                // cahce is not empty, so flush it.
                stringBuilder.Append(value, i, n);
            }

            // add end quote mark
            stringBuilder.Append('"');

            return stringBuilder.ToString();
        }
    }
}
